package com.flow.framework.module.call.rpc.factory;

import com.flow.framework.common.exception.CheckedException;
import feign.InvocationHandlerFactory;
import feign.Target;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

import static feign.Util.checkNotNull;

/**
 * feign使用jdk proxy调用时创建InvocationHandler使用的工厂类，参考：feign.InvocationHandlerFactory.Default
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/2/19
 */
@Slf4j
public class FeignInvocationHandlerFactory implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
        return new FeignInvocationHandlerFactory.FeignInvocationHandler(target, dispatch);
    }

    static class FeignInvocationHandler implements InvocationHandler {

        private final Target target;
        private final Map<Method, MethodHandler> dispatch;

        FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
            this.target = checkNotNull(target, "target");
            this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("equals".equals(method.getName())) {
                try {
                    Object otherHandler =
                            args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return equals(otherHandler);
                } catch (IllegalArgumentException e) {
                    return false;
                }
            } else if ("hashCode".equals(method.getName())) {
                return hashCode();
            } else if ("toString".equals(method.getName())) {
                return toString();
            }

            try {
                return dispatch.get(method).invoke(args);
            } catch (Exception e) {
                log.error("feign proxy invoke error.", e);
                Throwable cause = e.getCause();
                if (cause instanceof CheckedException) {
                    throw cause;
                }
                throw e;
            }

        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof FeignInvocationHandlerFactory.FeignInvocationHandler) {
                FeignInvocationHandlerFactory.FeignInvocationHandler other = (FeignInvocationHandlerFactory.FeignInvocationHandler) obj;
                return target.equals(other.target);
            }
            return false;
        }

        @Override
        public int hashCode() {
            return target.hashCode();
        }

        @Override
        public String toString() {
            return target.toString();
        }
    }
}